package net.opentsdb.tools;

import java.util.*;

/**
 * @author Hujf
 * @title: ArgP
 * @date 2021-09-08 9:11
 * @description: 启动参数  命令行参数解析器
 */
public final class ArgP {

    /**
     * Maps an option name (e.g, {@code "--foo"}) to a 2-element array
     * {@code ["META", "Help string"]}
     */
    private final HashMap<String, String[]> options
            = new HashMap<String, String[]>();

    /**
     * Maps an option name to the value parsed for this option.
     * The value can be {@code null}.
     */
    private HashMap<String, String> parsed;

    public ArgP() {
    }

    public void addOption(final String name, final String meta, final String help) {
        if (name.isEmpty()) {
            throw new IllegalArgumentException("empty name");
        } else if (name.charAt(0) != '-') {
            throw new IllegalArgumentException("name must start with a `-': " + name);
        } else if (meta != null && meta.isEmpty()) {
            throw new IllegalArgumentException("empty meta");
        } else if (help.isEmpty()) {
            throw new IllegalArgumentException("empty help");
        }
        final String[] prev = options.put(name, new String[]{meta, help});
        if (prev != null) {
            // Undo the `put' above.
            options.put(name, prev);
            throw new IllegalArgumentException("Option " + name + " already defined"
                    + " in " + this);
        }
    }

    public void addOption(final String name, final String help) {
        addOption(name, null, help);
    }

    public String[] parse(String[] args) {
        parsed = new HashMap<String, String>(options.size());
        ArrayList<String> unparsed = null;
        for (int i = 0; i < args.length; i++) {
            final String arg = args[i];
            String[] opt = options.get(arg);
            if (opt != null) {
                if (opt[0] != null) {
                    if (++i < args.length) {
                        parsed.put(arg, args[i]);
                    } else {
                        throw new IllegalArgumentException("丢失参数" + arg);
                    }
                } else {
                    parsed.put(arg, null);
                }
                continue;
            }

            final int equal = arg.indexOf('=', 1);
// Looks like so.
            if (equal > 0) {
                final String name = arg.substring(0, equal);
                opt = options.get(name);
                if (opt != null) {
                    parsed.put(name, arg.substring(equal + 1, arg.length()));
                    continue;
                }
            }
            // Not a flag.
            if (unparsed == null) {
                unparsed = new ArrayList<String>(args.length - i);
            }
            if (!arg.isEmpty() && arg.charAt(0) == '-') {
                // `--'
                if (arg.length() == 2 && arg.charAt(1) == '-') {
                    for (i++; i < args.length; i++) {
                        unparsed.add(args[i]);
                    }
                    break;
                }
                throw new IllegalArgumentException("Unrecognized option " + arg);
            }
            unparsed.add(arg);
        }
        if (unparsed != null) {
            return unparsed.toArray(new String[unparsed.size()]);
        } else {
            return new String[0];
        }
    }

    public String get(String name, String default_v) {
        final String value = get(name);
        return value == null ? default_v : value;
    }

    public String get(String name) {
        if (!options.containsKey(name)) {
            throw new IllegalArgumentException("Unknown option " + name);
        } else if (parsed == null) {
            throw new IllegalStateException("parse() wasn't called");
        }
        return parsed.get(name);
    }

    public String usage() {
        final StringBuilder buf = new StringBuilder(16 * options.size());
        addUsageTo(buf);
        return buf.toString();
    }

    public void addUsageTo(final StringBuilder buf) {
        final ArrayList<String> names = new ArrayList<String>(options.keySet());
        Collections.sort(names);
        int max_length = 0;
        for (final String name : names) {
            final String[] opt = options.get(name);
            final int length = name.length()
                    + (opt[0] == null ? 0 : opt[0].length() + 1);
            if (length > max_length) {
                max_length = length;
            }
        }
        for (final String name : names) {
            final String[] opt = options.get(name);
            int length = name.length();
            buf.append("  ").append(name);
            if (opt[0] != null) {
                length += opt[0].length() + 1;
                buf.append('=').append(opt[0]);
            }
            for (int i = length; i <= max_length; i++) {
                buf.append(' ');
            }
            buf.append(opt[1]).append('\n');
        }
    }

    public Map<String, String> getParsed() {
        return this.parsed;
    }
}
